<?php
require_once 'Class.php';
require_once 'Config.php';
require_once 'Function.php';

use PhpParser\NodeVisitorAbstract;
use PhpParser\NodeTraverser;

class ViewVisitor extends NodeVisitorAbstract
{
    public $count;
    public function beforeTraverse(array $nodes){
        print("beforeTraverse:\n");
        $this->count=0;
        print_r($nodes);
        print("\n\n");
    }
    public function enterNode(\PhpParser\Node $node){
        print("enterNode:\n");
        print_r($node);
        $this->count++;
        print("\n\n");
    }
    public function leaveNode(\PhpParser\Node $node){
        print("leaveNode:\n");
        print_r($node);
        print("\n\n");
    }
    public function afterTraverse(array $nodes){
        print("afterTraverse:\n");
        print_r($nodes);
        print($this->count);
        print("\n\n");
    }
}

class func_call_map_Visitor extends NodeVisitorAbstract
{
    public $func_call_map;
    public $function_name;
    public $stmt_num;
    public $var_list;
    public function beforeTraverse(array $nodes){
        $this->func_call_map = new Func_Call_Map;
        $this->stmt_num = 0;
        $this->function_name = $nodes[0]->name->name;
        $this->func_call_map->function_name = $this->function_name;
        $this->var_list = [];
    }
    public function enterNode(\PhpParser\Node $node){
        global $stmt_func;
        if($node->gettype() === 'Expr_FuncCall'){
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            foreach ($node->args as $key=>$value){
                if ($value->value->gettype() === 'Expr_Variable')
                    $func_call_info->params[$key] = $value->value->name;;
            }
            $this->func_call_map->call_function[$node->name->parts[0]][] = $func_call_info;
        }elseif(isset($stmt_func[$node->gettype()])){
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            if (in_array($node->gettype(), ['Expr_Eval']) && in_array($node->expr->gettype(), ['Expr_ArrayDimFetch','Expr_Variable'])){
                if($node->expr->gettype() === 'Expr_Variable'){
                    $func_call_info->params[1] = $node->expr->name;
                }elseif ($node->expr->gettype() === 'Expr_ArrayDimFetch'){
                    $func_call_info->params[1] = $node->expr->var->name . "|" . $node->expr->dim->value;
                }
            }
            if (in_array($node->gettype(), ['Stmt_Echo']))
                foreach ($node->exprs as $key=>$value)
                    if(in_array($value->gettype(),['Expr_ArrayDimFetch','Expr_Variable']))
                        $func_call_info->params[$key] = $value->value->name;;
            $this->func_call_map->call_function[$stmt_func[$node->gettype()]][] = $func_call_info;
        }elseif ($node->gettype() === 'Expr_New'){
            if($node->getAttribute('parent')->gettype() === 'Expr_Assign'){
                $this->var_list[$node->getAttribute('previous')->name] = $node->class->parts[0];
            }
        }elseif ($node->gettype() === 'Expr_MethodCall'){
            if($node->var->gettype() === 'Expr_Variable'){
                $class_name = $this->var_list[$node->var->name];
                $method_name = $node->name->name;
                $func_call_info = new func_call_info;
                $func_call_info->stmt_num = $this->stmt_num;
                foreach ($node->args as $key=>$value){
                    if ($value->value->gettype() === 'Expr_Variable')
                        $func_call_info->params[$key] = $value->value->name;;
                }
                $this->func_call_map->call_function[$method_name][] = $func_call_info;
            }elseif ($node->var->gettype() === 'Expr_MethodCall'){
                return NodeTraverser::DONT_TRAVERSE_CHILDREN;
            }
        }elseif($node->gettype() === 'Expr_StaticCall'){
            $class_name = $node->class->parts[0];
            $method_name = $node->name->name;
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            foreach ($node->args as $key=>$value){
                if ($value->value->gettype() === 'Expr_Variable')
                    $func_call_info->params[$key] = $value->value->name;;
            }
            $this->func_call_map->call_function[$method_name][] = $func_call_info;
        }elseif ($node->gettype() === 'Param'){
            $this->func_call_map->params[] = $node->var->name;
        }

        if (is_object($node)){
            if (strpos($node->gettype(),'Stmt') !== false){
                $this->stmt_num += 1;
            }
        }
    }
    public function afterTraverse(array $nodes){
        global $func_call_map;
        if ($this->func_call_map->function_name){
            $func_call_map[$this->function_name] = $this->func_call_map;
        }
    }
}

class method_call_map_Visitor extends NodeVisitorAbstract
{
    public $func_call_map;
    public $function_name;
    public $stmt_num;
    public $var_list;
    public function beforeTraverse(array $nodes){
        $this->func_call_map = new Func_Call_Map;
        $this->stmt_num = 0;
        $class_name = $nodes[0]->getAttribute("class_name");
        $this->function_name = $nodes[0]->name->name;
        $this->func_call_map->function_name = $this->function_name;
        $this->var_list = [];
        $this->var_list['this'] = $class_name;
    }
    public function enterNode(\PhpParser\Node $node){
        global $stmt_func;
        if($node->gettype() === "Param"){
            $this->func_call_map->params[] = $node->var->name;
            $this->var_list[$node->var->name] = $node->type->parts[1];
        }elseif($node->gettype() === 'Expr_FuncCall'){
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            foreach ($node->args as $key=>$value){
                if ($value->value->gettype() === 'Expr_Variable')
                    $func_call_info->params[$key] = $value->value->name;;
            }
            $this->func_call_map->call_function[$node->name->parts[0]][] = $func_call_info;
        }elseif(isset($stmt_func[$node->gettype()])){
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            if (in_array($node->gettype(), ['Expr_Eval']) && in_array($node->expr->gettype(), ['Expr_ArrayDimFetch','Expr_Variable']))
                if($node->expr->gettype() === 'Expr_Variable'){
                    $func_call_info->params[1] = $node->expr->name;
                }elseif ($node->expr->gettype() === 'Expr_ArrayDimFetch'){
                    $func_call_info->params[1] = $node->expr->var->name . "|" . $node->expr->dim->value;
                }
            if (in_array($node->gettype(), ['Stmt_Echo']))
                foreach ($node->exprs as $key=>$value)
                    if(in_array($value->gettype(),['Expr_ArrayDimFetch','Expr_Variable']))
                        $func_call_info->params[$key] = $value->value->name;;
            $this->func_call_map->call_function[$stmt_func[$node->gettype()]][] = $func_call_info;
        }elseif ($node->gettype() === 'Expr_New'){
            if($node->getAttribute('parent')->gettype() === 'Expr_Assign'){
                $this->var_list[$node->getAttribute('previous')->name] = $node->class->parts[0];
            }
        }elseif ($node->gettype() === 'Expr_MethodCall'){
            $class_name = $this->var_list[$node->var->name];
            $method_name = $node->name->name;
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            foreach ($node->args as $key=>$value){
                if ($value->value->gettype() === 'Expr_Variable')
                    $func_call_info->params[$key] = $value->value->name;;
            }
            $this->func_call_map->call_function[$method_name][] = $func_call_info;
        }elseif($node->gettype() === 'Expr_StaticCall'){
            $class_name = $node->class->parts[0];
            $method_name = $node->name->name;
            $func_call_info = new func_call_info;
            $func_call_info->stmt_num = $this->stmt_num;
            foreach ($node->args as $key=>$value){
                if ($value->value->gettype() === 'Expr_Variable')
                    $func_call_info->params[$key] = $value->value->name;;
            }
            $this->func_call_map->call_function[$method_name][] = $func_call_info;
        }

        if (is_object($node)){
            if (strpos($node->gettype(),'Stmt') !== false){
                $this->stmt_num += 1;
            }
        }
    }
    public function afterTraverse(array $nodes){
        global $func_call_map;
        if ($this->func_call_map->function_name){
            $func_call_map[$this->function_name] = $this->func_call_map;
        }
    }
}

class deal_class_Visitor extends NodeVisitorAbstract{
    public $class_name;
    public function enterNode(\PhpParser\Node $node){
        if($node->gettype() === 'Stmt_Class'){
            $this->class_name = $node->name->name;
        }elseif($node->gettype() === 'Stmt_ClassMethod'){
            $node->setAttribute('class_name',$this->class_name);
        }
    }
}

class sensitive_func_Visitor extends NodeVisitorAbstract{
    public $func_call_map;
    public $function_name;
    public $var_list;
    public $is_assign;//判断是否在赋值语句中
    public $tag; //敏感值，如果tag为0表示不敏感，tag为1表示返回值与函数的参数有关，tag为2表示返回值与source点有关，tag为4表示与全局变量有关，其表示方式为二进制，与Linux文件权限表示方式相同。
    public $global_var;
    public function beforeTraverse(array $nodes){
        global $func_call_map;
        $this->function_name = $nodes[0]->name->name;
        foreach ($func_call_map as $value){
            if($value->function_name === $this->function_name){
                $this->func_call_map = $value;
            }
        }
        $this->var_list = [];
        foreach ($nodes[0]->params as $param){
            $this->var_list[$param->var->name] = 1;
        }
        $this->is_assign = 0;
        $this->global_var = [];
    }
    public function enterNode(\PhpParser\Node $node){
        if($this->is_assign === 1 && $node->gettype() === 'Expr_Variable'){
            if($this->var_list[$node->name]){
                $this->tag |= $this->var_list[$node->name];
            }elseif (in_array($node->name, Sources::$V_USERINPUT)){
                $this->tag |= 2;
            }elseif (in_array($node->name, $this->global_var)){
                $this->tag |= 4;
            }
        }

        if($node->gettype() === "Stmt_Global"){
            foreach ($node->vars as $var){
                $this->global_var[] = $var->name;
            }
        }
        if($node->gettype() === 'Expr_Assign'){
            $this->is_assign = 1;
            $this->var_list[$node->var->name] = 0;
            $this->tag = 0;
        }

        if ($node->gettype() === 'Stmt_Return'){
            if($node->expr->gettype() === 'Expr_Variable' && isset($this->var_list[$node->expr->name])){
                $this->func_call_map->tag = $this->var_list[$node->expr->name];
            }
        }
    }

    public function leaveNode(\PhpParser\Node $node){
        if($node->gettype() === 'Expr_Assign'){
            $this->is_assign = 0;
            $this->var_list[$node->var->name] = $this->tag;
        }
    }

    public function afterTraverse(array $nodes){
        global $func_call_map;
        if ($this->func_call_map->function_name){
            $func_call_map[$this->function_name] = $this->func_call_map;
        }
    }
}


class sensitive_method_Visitor extends NodeVisitorAbstract{
    public $func_call_map;
    public $function_name;
    public $var_list;
    public $is_assign;//判断是否在赋值语句中
    public $tag; //敏感值，如果tag为0表示不敏感，tag为1表示返回值与函数的参数有关，tag为2表示返回值与source点有关，tag为4表示与全局变量有关，其表示方式为二进制，与Linux文件权限表示方式相同。
    public $global_var;
    public function beforeTraverse(array $nodes){
        global $func_call_map;
        $class_name = $nodes[0]->getAttribute("class_name");
        $this->function_name = $nodes[0]->name->name;
        foreach ($func_call_map as $value){
            if($value->function_name === $this->function_name){
                $this->func_call_map = $value;
            }
        }
        $this->var_list = [];
        foreach ($nodes[0]->params as $param){
            $this->var_list[$param->var->name] = 1;
        }
        $this->is_assign = 0;
        $this->global_var = [];
    }
    public function enterNode(\PhpParser\Node $node){
        if($this->is_assign === 1 && $node->gettype() === 'Expr_Variable'){
            if($this->var_list[$node->name]){
                $this->tag |= $this->var_list[$node->name];
            }elseif (in_array($node->name, Sources::$V_USERINPUT)){
                $this->tag |= 2;
            }elseif (in_array($node->name, $this->global_var)){
                $this->tag |= 4;
            }
        }

        if($node->gettype() === "Stmt_Global"){
            foreach ($node->vars as $var){
                $this->global_var[] = $var->name;
            }
        }
        if($node->gettype() === 'Expr_Assign'){
            $this->is_assign = 1;
            $this->var_list[$node->var->name] = 0;
            $this->tag = 0;
        }
        if ($node->gettype() === 'Stmt_Return'){
            if($node->expr->gettype() === 'Expr_Variable' && isset($this->var_list[$node->expr->name])){
                $this->func_call_map->tag = $this->var_list[$node->expr->name];
            }
        }
    }

    public function leaveNode(\PhpParser\Node $node){
        if($node->gettype() === 'Expr_Assign'){
            $this->is_assign = 0;
            $this->var_list[$node->var->name] = $this->tag;
        }
    }

    public function afterTraverse(array $nodes){
        global $func_call_map;
        if ($this->func_call_map->function_name){
            $func_call_map[$this->function_name] = $this->func_call_map;
        }
    }
}


class get_CFG_Visitor extends NodeVisitorAbstract{
    public $position; //存放当前语句的位置，即处于哪一层if，如果处于第一层if就是if，如果处于第二次if就是if_if，通过下划线来分割
    public $return_stack; //存放返回点的块编号，用栈来存储，就像函数调用一样。
    public $block_num;//存放当前块号
    public $block; //当前块
    public $CFG;
    public $stmt_num;

    public function beforeTraverse(array $nodes){
        $this->CFG = new CFG_class;
        $this->position = '';
        $this->block_num = 0;
        $this->stmt_num = 0;
        $this->block = [];
        $this->return_stack = [];
    }
    public function enterNode(\PhpParser\Node $node){
        if($node->gettype() === 'Stmt_If'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num + 1;
            $relation->condition = $node->cond;
            $this->return_stack[] = $this->block_num;
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_Else'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = end($this->return_stack);
            $relation->to = $this->block_num + 1;
            $relation->condition = 'else';
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_ElseIf'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = end($this->return_stack);
            $relation->to = $this->block_num + 1;
            $relation->condition = $node->cond;
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];

        }elseif($node->gettype() === 'Stmt_While'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = '';
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_Foreach'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = '';
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_For'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = '';
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_Do'){
            $this->block[$this->stmt_num] = $node;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = '';
            $this->block_num += 1;
            $this->CFG->blocks[] = $this->block;
            $this->CFG->map[] = $relation;
            $this->block = [];
        }elseif($node->gettype() !== 'Stmt_Function' && $node->gettype() !== 'Stmt_ClassMethod' && strpos($node->gettype(),'Stmt') !== false){
            $this->block[$this->stmt_num] = $node;
        }

        if (is_object($node)){
            if (strpos($node->gettype(),'Stmt') !== false){
                $this->stmt_num += 1;
            }
        }


    }

    public function leaveNode(\PhpParser\Node $node){
        if($node->gettype() === 'Stmt_If'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num + 1;
            $relation->condition = null;
            $this->CFG->blocks[] = $this->block;
            $this->block = [];
            $this->CFG->map[] = $relation;
            if(!isset($node->else)){
                $relation = new relation;
                $relation->from = array_pop ($this->return_stack);
                $relation->to = $this->block_num + 1;
                $relation->condition = null;
                $this->CFG->map[] = $relation;
                $this->block_num += 1;   //解释一下为什么if语句退出时候block_num要+1，因为想想控制流程图，if,else是一个分叉，而你看看AST树就知道，if退出的时候是整个else和elseif都退出的时候，也就是说if退出的时候分叉汇合，必须开始记载下一个块
            }


        }elseif($node->gettype() === 'Stmt_Else'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num + 1;
            $relation->condition = 'else';
            $this->CFG->map[] = $relation;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->block = [];

        }elseif($node->gettype() === 'Stmt_ElseIf'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num + 1;
            $relation->condition = $node->cond;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->CFG->map[] = $relation;
            $this->block = [];

        }elseif($node->gettype() === 'Stmt_While'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num;
            $relation->condition = $node->cond;
            $this->CFG->map[] = $relation;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = 'no';
            $this->CFG->map[] = $relation;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_Foreach'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num;
            $relation->condition = 'foreach|'.$node->expr->name;
            $this->CFG->map[] = $relation;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = 'no';
            $this->CFG->map[] = $relation;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_For'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num;
            $relation->condition = $node->cond;
            $this->CFG->map[] = $relation;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = 'no';
            $this->CFG->map[] = $relation;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->block = [];
        }elseif($node->gettype() === 'Stmt_Do'){
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num;
            $relation->condition = $node->cond;
            $this->CFG->map[] = $relation;
            $relation = new relation;
            $relation->from = $this->block_num;
            $relation->to = $this->block_num+1;
            $relation->condition = 'no';
            $this->CFG->map[] = $relation;
            if($this->block) {
                $this->block_num += 1;
                $this->CFG->blocks[] = $this->block;
            }
            $this->block = [];
        }

    }

    public function afterTraverse(array $nodes){

        global $CFG;
        if($this->block != NULL){
            $this->CFG->blocks[] = $this->block;
        }

        $CFG = $this->CFG;
    }
}

class newVisitor extends NodeVisitorAbstract
{
    public $count;
    public function beforeTraverse(array $nodes){

    }
    public function enterNode(\PhpParser\Node $node){

    }
    public function leaveNode(\PhpParser\Node $node){

    }
    public function afterTraverse(array $nodes){

    }
}